﻿using HelixToolkit.Wpf;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace PlyViewer
{
    public class HighPerformancePointCloudRenderer
    {
        private const int MAX_POINTS_PER_BATCH = 50000;

        private List<PointsVisual3D> pointVisuals = new List<PointsVisual3D>();
        public int DisplayedPointCount { get; private set; }

        // 存储全局的Z值范围，确保颜色一致性
        private double globalMinZ = 0;
        private double globalMaxZ = 0;

        // 存储全局边界用于空间抽样
        private double globalMinX = 0, globalMaxX = 0, globalMinY = 0, globalMaxY = 0;

        public ModelVisual3D CreateOptimizedVisual(PlyPointCloud pointCloud, double pointSize, double detailLevel, bool useHeightColoring)
        {
            var stopwatch = Stopwatch.StartNew();

            // 清除之前的可视化
            pointVisuals.Clear();

            var container = new ModelVisual3D();

            if (pointCloud.Points.Count == 0)
            {
                Debug.WriteLine("警告: 点云中没有点");
                return container;
            }

            // 计算全局Z值范围
            CalculateGlobalZRange(pointCloud.Points);

            // 计算全局边界用于空间抽样
            CalculateGlobalBounds(pointCloud.Points);

            // 根据细节级别确定目标点数
            int targetPointCount = CalculateTargetPointCount(pointCloud.Points.Count, detailLevel);

            // 准备渲染数据
            var renderData = PrepareRenderData(pointCloud, targetPointCount);
            DisplayedPointCount = renderData.Points.Count;

            Debug.WriteLine($"渲染优化: 原始点数 {pointCloud.Points.Count} -> 显示点数 {DisplayedPointCount}");
            if (useHeightColoring)
            {
                Debug.WriteLine($"全局Z范围: {globalMinZ:F4} 到 {globalMaxZ:F4}");
            }

            // 使用优化的点云着色方法
            if (useHeightColoring)
            {
                // 使用高度带着色方法
                CreateHeightBandPointCloud(container, renderData.Points, pointSize);
            }
            else
            {
                // 白色点云
                CreateWhitePointCloud(container, renderData.Points, pointSize);
            }

            stopwatch.Stop();
            Debug.WriteLine($"点云渲染完成, 耗时: {stopwatch.ElapsedMilliseconds}ms");

            return container;
        }

        private void CalculateGlobalZRange(List<Point3D> points)
        {
            globalMinZ = double.MaxValue;
            globalMaxZ = double.MinValue;

            // 使用并行计算加速Z值范围计算
            Parallel.Invoke(
                () =>
                {
                    double localMin = double.MaxValue;
                    foreach (var point in points)
                    {
                        localMin = Math.Min(localMin, point.Z);
                    }
                    globalMinZ = localMin;
                },
                () =>
                {
                    double localMax = double.MinValue;
                    foreach (var point in points)
                    {
                        localMax = Math.Max(localMax, point.Z);
                    }
                    globalMaxZ = localMax;
                }
            );

            // 如果所有Z值相同，稍微扩展范围以避免除零错误
            if (Math.Abs(globalMaxZ - globalMinZ) < 0.0001)
            {
                globalMinZ -= 0.5;
                globalMaxZ += 0.5;
                Debug.WriteLine("所有点Z值相同，扩展Z范围以避免除零错误");
            }
        }

        private void CalculateGlobalBounds(List<Point3D> points)
        {
            globalMinX = double.MaxValue;
            globalMaxX = double.MinValue;
            globalMinY = double.MaxValue;
            globalMaxY = double.MinValue;

            // 使用并行计算加速边界计算
            var results = new ConcurrentBag<(double minX, double maxX, double minY, double maxY)>();

            var partitioner = Partitioner.Create(0, points.Count, 10000);
            Parallel.ForEach(partitioner, range =>
            {
                double localMinX = double.MaxValue, localMaxX = double.MinValue;
                double localMinY = double.MaxValue, localMaxY = double.MinValue;

                for (int i = range.Item1; i < range.Item2; i++)
                {
                    var point = points[i];
                    localMinX = Math.Min(localMinX, point.X);
                    localMaxX = Math.Max(localMaxX, point.X);
                    localMinY = Math.Min(localMinY, point.Y);
                    localMaxY = Math.Max(localMaxY, point.Y);
                }

                results.Add((localMinX, localMaxX, localMinY, localMaxY));
            });

            // 合并结果
            globalMinX = results.Min(r => r.minX);
            globalMaxX = results.Max(r => r.maxX);
            globalMinY = results.Min(r => r.minY);
            globalMaxY = results.Max(r => r.maxY);
        }

        private int CalculateTargetPointCount(int originalCount, double detailLevel)
        {
            // 根据点云规模智能调整抽样策略
            if (originalCount <= 10000)
            {
                // 小点云（≤1万点）：不进行抽样，保持原始细节
                return originalCount;
            }
            else if (originalCount <= 200000)
            {
                // 中等点云（1-50万点）：按比例抽样（10%-100%）
                if (detailLevel <= 0.1) return (int)(originalCount * 0.1);  // 10%
                if (detailLevel <= 0.2) return (int)(originalCount * 0.3);  // 30%
                if (detailLevel <= 0.5) return (int)(originalCount * 0.5);  // 50%
                if (detailLevel <= 1) return (int)(originalCount * 1);  // 80%
                return originalCount;
            }
            else
            {
                // 大点云（>100万点）：更激进的抽样（5%-40%），并设置上限
                if (detailLevel <= 0.1) return Math.Min(100000, (int)(originalCount * 0.05));  // 5%, 最多10万
                if (detailLevel <= 0.2) return Math.Min(200000, (int)(originalCount * 0.3));   // 10%, 最多30万
                if (detailLevel <= 0.5) return Math.Min(300000, (int)(originalCount * 0.6));   // 20%, 最多50万
                return Math.Min(500000, originalCount);  // 最多100万点
            }
        }

        private RenderData PrepareRenderData(PlyPointCloud pointCloud, int targetPointCount)
        {
            var result = new RenderData();

            if (pointCloud.Points.Count <= targetPointCount)
            {
                // 不需要下采样
                result.Points = pointCloud.Points;
                return result;
            }

            // 根据点云特征选择最佳抽样策略
            if (pointCloud.Points.Count > 6000000)
            {
                // 大点云使用空间网格抽样
                result.Points = SpatialDownsampling(pointCloud.Points, targetPointCount);
            }
            else
            {
                // 中小点云使用随机抽样
                result.Points = RandomSampling(pointCloud.Points, targetPointCount);
            }

            return result;
        }

        private List<Point3D> SpatialDownsampling(List<Point3D> points, int targetCount)
        {
            if (points.Count <= targetCount) return points;

            // 计算点云边界
            double rangeX = globalMaxX - globalMinX;
            double rangeY = globalMaxY - globalMinY;
            double rangeZ = globalMaxZ - globalMinZ;

            // 根据目标点数动态计算网格大小
            double volume = rangeX * rangeY * rangeZ;
            if (volume < 0.001) volume = 0.001; // 避免除零

            double pointsPerUnit = Math.Pow(targetCount / volume, 1.0 / 3.0);
            double gridSize = Math.Max(1.0 / pointsPerUnit/3, 0.001); // 最小网格大小1mm
        
            Debug.WriteLine($"空间下采样: 目标{targetCount}点, 网格大小: {gridSize:F4}");

            // 使用空间网格进行均匀抽样
            var grid = new Dictionary<(int, int, int), Point3D>();

            foreach (var point in points)
            {
                int gridX = (int)((point.X - globalMinX) / gridSize);
                int gridY = (int)((point.Y - globalMinY) / gridSize);
                int gridZ = (int)((point.Z - globalMinZ) / gridSize);

                var cellKey = (gridX, gridY, gridZ);

                // 每个网格单元只保留一个点
                if (!grid.ContainsKey(cellKey))
                {
                    grid[cellKey] = point;
                }
            }

            var sampledPoints = grid.Values.ToList();

            // 如果抽样后点数仍然过多，进行二次随机抽样
            if (sampledPoints.Count > targetCount * 1.2)
            {
                sampledPoints = RandomSampling(sampledPoints, targetCount);
            }

            Debug.WriteLine($"空间下采样完成: {points.Count} -> {sampledPoints.Count} 点");
            return sampledPoints;
        }

        private List<Point3D> RandomSampling(List<Point3D> points, int targetCount)
        {
            if (points.Count <= targetCount) return points;

            var random = new Random();
            var sampled = new List<Point3D>(targetCount);
            var indices = new HashSet<int>();

            while (sampled.Count < targetCount && indices.Count < points.Count)
            {
                int index = random.Next(points.Count);
                if (indices.Add(index))
                {
                    sampled.Add(points[index]);
                }
            }

            Debug.WriteLine($"随机抽样完成: {points.Count} -> {sampled.Count} 点");
            return sampled;
        }

        private void CreateHeightBandPointCloud(ModelVisual3D container, List<Point3D> points, double pointSize)
        {
            // 使用高度带方法，将点云按高度分成多个带，每个带使用不同的颜色
            const int colorBands = 16; // 使用16个颜色带，提供足够的分辨率

            // 按高度分组
            var heightGroups = new List<List<Point3D>>();
            for (int i = 0; i < colorBands; i++)
            {
                heightGroups.Add(new List<Point3D>());
            }

            // 并行分组 - 使用更高效的方法
            var groupLocks = new object[colorBands];
            for (int i = 0; i < colorBands; i++)
            {
                groupLocks[i] = new object();
            }

            // 使用分区并行处理
            var partitioner = Partitioner.Create(0, points.Count);
            Parallel.ForEach(partitioner, range =>
            {
                for (int i = range.Item1; i < range.Item2; i++)
                {
                    var point = points[i];
                    double ratio = (point.Z - globalMinZ) / (globalMaxZ - globalMinZ);
                    ratio = Math.Max(0, Math.Min(1, ratio));

                    int band = (int)(ratio * (colorBands - 1));
                    lock (groupLocks[band])
                    {
                        heightGroups[band].Add(point);
                    }
                }
            });

            // 为每个高度带创建点云
            int totalPoints = 0;
            for (int i = 0; i < colorBands; i++)
            {
                if (heightGroups[i].Count > 0)
                {
                    double ratio = i / (double)(colorBands - 1);
                    var color = GetColorByRatio(ratio);

                    // 分批创建点云，避免单个Visual包含过多点
                    int batchCount = (heightGroups[i].Count + MAX_POINTS_PER_BATCH - 1) / MAX_POINTS_PER_BATCH;

                    for (int batch = 0; batch < batchCount; batch++)
                    {
                        int startIndex = batch * MAX_POINTS_PER_BATCH;
                        int endIndex = Math.Min(startIndex + MAX_POINTS_PER_BATCH, heightGroups[i].Count);
                        int count = endIndex - startIndex;

                        var batchPoints = heightGroups[i].GetRange(startIndex, count);

                        var pointsVisual = new PointsVisual3D
                        {
                            Size = pointSize,
                            Color = color
                        };

                        var pointCollection = new Point3DCollection(batchPoints);
                        pointsVisual.Points = pointCollection;

                        container.Children.Add(pointsVisual);
                        pointVisuals.Add(pointsVisual);
                    }

                    totalPoints += heightGroups[i].Count;
                }
            }

            Debug.WriteLine($"使用高度带着色，{colorBands}个颜色带，总点数: {totalPoints}");
        }

        private void CreateWhitePointCloud(ModelVisual3D container, List<Point3D> points, double pointSize)
        {
            // 分批创建白色点云
            int batchCount = (points.Count + MAX_POINTS_PER_BATCH - 1) / MAX_POINTS_PER_BATCH;

            for (int batch = 0; batch < batchCount; batch++)
            {
                int startIndex = batch * MAX_POINTS_PER_BATCH;
                int endIndex = Math.Min(startIndex + MAX_POINTS_PER_BATCH, points.Count);
                int count = endIndex - startIndex;

                var batchPoints = points.GetRange(startIndex, count);

                var pointsVisual = new PointsVisual3D
                {
                    Size = pointSize,
                    Color = Colors.White
                };

                var pointCollection = new Point3DCollection(batchPoints);
                pointsVisual.Points = pointCollection;

                container.Children.Add(pointsVisual);
                pointVisuals.Add(pointsVisual);
            }

            Debug.WriteLine($"创建白色点云，{batchCount}个批次，总点数: {points.Count}");
        }

        private Color GetColorByRatio(double ratio)
        {
            // 使用优化的Jet色彩映射
            // 蓝 -> 青 -> 绿 -> 黄 -> 红
            double r = 0, g = 0, b = 0;

            if (ratio < 0.25)
            {
                // 蓝到青
                r = 0;
                g = ratio / 0.25;
                b = 1;
            }
            else if (ratio < 0.5)
            {
                // 青到绿
                r = 0;
                g = 1;
                b = 1 - (ratio - 0.25) / 0.25;
            }
            else if (ratio < 0.75)
            {
                // 绿到黄
                r = (ratio - 0.5) / 0.25;
                g = 1;
                b = 0;
            }
            else
            {
                // 黄到红
                r = 1;
                g = 1 - (ratio - 0.75) / 0.25;
                b = 0;
            }

            // 确保颜色值在0-1范围内
            r = Math.Max(0, Math.Min(1, r));
            g = Math.Max(0, Math.Min(1, g));
            b = Math.Max(0, Math.Min(1, b));

            return Color.FromRgb(
                (byte)(r * 255),
                (byte)(g * 255),
                (byte)(b * 255));
        }

        public void UpdatePointSize(double newSize)
        {
            foreach (var visual in pointVisuals)
            {
                visual.Size = newSize;
            }
        }

        private class RenderData
        {
            public List<Point3D> Points { get; set; }
        }
    }
}